Candace Savonen - CCDL for ALSF

This notebook sets up the MAF data files as a combined file for ready comparison in 02-analyze-concordance.Rmd and also does some first line analyses from ready-made maftools functions.

It is the first notebook in this series which addresses issue # 30 in OpenPBTA.

Summary of the Set Up:

For both the Strelka2 and MuTect2 datasets, the data is imported by maftools::read.maf and the corresponding clinical data from pbta-histologies.tsv is added to the object. This is only done once as written (as the read.maf is very memory intensive) and each MuTect2 and Strelka2 are saved to an RDS file for faster and ready reloading.

For both Strelka2 and MuTect2 data, the following variables are calculated and added into the combined dataset for analysis in 02-analyze-concordance.Rmd.

Variables created:

  • vaf : Variant Allele Frequency = (t_alt_count) / (t_ref_count + t_alt_count).
  • mutation_id: Used to determine whether two variants calls are identical in both datasets. It is a concatenation of: Hugo_Symbol, change, Start_Position, and Tumor_Sample_Barcode (the sample ID).
  • base_change: variable that indicates the exact change in bases (e.g. ‘T>C’).
  • change: indicates the base_change information but groups together deletions, insertions, and long (more than a SNV) as their own groups.
  • coding: Summarize the BIOTYPE variable for whether or not it is a coding gene.

The output files from this notebook:

  • scratch/strelka2.RDS
  • scratch/mutect2.RDS
  • scratch/metadata_filtered_maf_samples.tsv
  • analyses/mutect2-vs-strelka2/plots/sample_cor_mutect2_vs_strelka2.pdf
  • analyses/mutect2-vs-strelka2/plots/sample_cor_mutect2_vs_strelka2.pdf
  • analyses/mutect2-vs-strelka2/results/combined_results.tsv

Usage

To run this from the command line, use:

Rscript -e "rmarkdown::render('analyses/mutect2-vs-strelka2/01-set-up.Rmd', 
                              clean = TRUE)"

This assumes you are in the top directory of the repository.

Set Up

# We need maftools - this will be added to the running Docker issue whenever it is up
if (!("maftools" %in% installed.packages())) {
  if (!requireNamespace("BiocManager", quietly = TRUE)) {
    install.packages("BiocManager")
  }
  BiocManager::install("maftools")
}

# Will need hexbin for the hex plot
if (!("hexbin" %in% installed.packages())) {
  install.packages("hexbin")
}

Get magrittr pipe

`%>%` <- dplyr::`%>%`

Directories and files

Path to the symlinked data obtained via bash download-data.sh.

data_dir <- file.path("..", "..", "data")
scratch_dir <- file.path("..", "..", "scratch")

Create output directories in this analysis folder.

if (!dir.exists("results")) {
  dir.create("results")
}
if (!dir.exists("plots")) {
  dir.create("plots")
}

Functions

Set up the function that will create the new variables from the maf objects.

Description of variables

The function, set_up_variables, will calculate the following variables from a maf object:
- Calculate VAF for each. The VAFs for each variation in each dataset are calculated by t_alt_count) / (t_ref_count + t_alt_count), as following the code used in maftools
- Create a base_change variable that indicates the exact change in bases.
- Create a change variable for each which indicates the base_change information but groups together deletions, insertions, and long (more than a SNV) as their own groups.
- Make a mutation id by concatenating Hugo_Symbol, change, Start_Position, and Tumor_Sample_Barcode (the sample ID).
We will use this variable to determine whether two variants calls are identical in both datasets. - Summarize the BIOTYPE variable for whether or not it is a coding gene.

set_up_variables <- function(maf = NULL) {
  # Creates these new variables from a maf object provided: VAF, mutation_id, 
  # base_change, change, coding.
  #
  # Args:
  #   maf: a maf object to create new variables from
  #
  # Returns:
  #   a data.frame with all the original information in the `@data` part of the 
  #   maf object but with these new variables: VAF, mutation_id, base_change, 
  #   change, coding.

  # Extract the data part of the maf object, put it through a dplyr pipe. 
  df <- maf@data
  df %>%
    dplyr::mutate(
      # Calculate the variant allele frequency
      vaf = as.numeric(t_alt_count) / (as.numeric(t_ref_count) +
                                         as.numeric(t_alt_count)),
      # Create a base_change variable
      base_change = paste0(Reference_Allele, ">", Allele),
      
      # Create a variable that notes whether the variant is in a 
      # coding/non-coding region
      coding = dplyr::case_when(
        BIOTYPE != "protein_coding" ~ "non-coding",
        TRUE ~ "protein_coding"
      )
    ) %>%
    dplyr::mutate(
      # From the base_change variable, summarize insertions, deletions, and 
      # changes that are more than one base into their own groups.
      change = dplyr::case_when(
      grepl("^-", base_change) ~ "insertion",
      grepl("-$", base_change) ~ "deletion",
      nchar(base_change) > 3 ~ "long_change",
      TRUE ~ base_change
    )) %>%
    dplyr::mutate(
      # Create the mutation id based on the change variable as well as the 
      # gene symbol, start position, and sample ID. 
      mutation_id = paste0(
        Hugo_Symbol, "_",
        change, "_",
        Start_Position, "_",
        Tumor_Sample_Barcode
      )
    ) %>%
    # Get rid of any variables that have completely NAs.
    dplyr::select(-which(apply(is.na(.), 2, all)))
}

Summarize and compare maf objects function

This function uses the maftools summary functions, maftools::getGeneSummary or maftools::getSampleSummary, to summarize and compare two maf objects. These maftools summary functions obtain the number of variants of each classification type on either a gene or sample level (this is taked from the information in the Variant_Classification field in the original maf file.) This field has the translation effect of the variant, e.g. “Frame_Shift_Del”.

correlate_maf_summaries <- function(maf1 = NULL, maf2 = NULL, 
                                    maf1_name = "maf1", maf2_name = "maf2", 
                                    summarize_by = "gene") {
  # Takes two maf files, summarizes them by gene or sample, combines them by 
  # a full join, and correlates the summaries using Pearson's and Spearman's
  # correlations. 
  #
  # Args:
  #   maf1: a first maf object to summarize and compare to maf2
  #   maf2: a second maf object to summarize and compare to maf1
  #   maf1_name: a character string that indicates what label you would like to 
  #   use for maf1's data. 
  #   maf2_name: a character string that indicates what label you would like to 
  #   use for maf2's data. 
  #   summarize_by: a single character string signifying either "gene" or "sample"
  #       This will indicate whether to summarize compare by gene or sample. 
  #       "gene" will summarize both maf1 and maf2 using maftools::getGeneSummary
  #       "sample" will summarize both maf1 and maf2 using maftools::getSampleSummary
  # Returns:
  #   a data.frame that contains the correlation r and p values for both 
  #   maf1 and maf2 based on the numbers of variants of each class.
  
  # Summarize the maf objects by the specified argument
  if (summarize_by == "gene") {
    maf1_sum <- maftools::getGeneSummary(maf1)
    maf2_sum <- maftools::getGeneSummary(maf2)
    key <- "Hugo_Symbol"
  }
  if (summarize_by == "sample") {
    maf1_sum <- maftools::getSampleSummary(maf1)
    maf2_sum <- maftools::getSampleSummary(maf2)
    key <- "Tumor_Sample_Barcode"
  }
  
  # Do a full join of both summaries.
  maf1_sum %>%
    dplyr::full_join(maf2_sum, by = key) %>%
    # Melt this data.frame so we can make it long format
    reshape2::melt(id = key) %>%
    dplyr::mutate(dataset = as.character(grepl(".x$", variable))) %>%
    # Make a new column that specifies what maf object the data is from
    dplyr::mutate(dataset = dplyr::recode(dataset,
      `TRUE` = maf1_name,
      `FALSE` = maf2_name,
    )) %>%
    # Gets rid of the ".x" and ".y" specifications of the column names
    dplyr::mutate(variable = gsub(".x$|.y$", "", variable)) %>%
    # Spreads the data based on the new dataset variable
    tidyr::spread("dataset", "value") %>%  
    
    # Get correlations by each type of variant classification
    dplyr::group_by(variable) %>%
    dplyr::summarize(pearson.cor = cor.test(strelka2, mutect2, method = "pearson")$estimate,
                     pearson.pval = cor.test(strelka2, mutect2, method = "pearson")$p.value,
                     spearman.cor = cor.test(strelka2, mutect2, method = "spearman")$estimate,
                     spearman.pval = cor.test(strelka2, mutect2, method = "pearson")$p.value) 
}

Read in the metadata information

Running maftools::read.maf takes a lot of computing power and time, so to avoid having to run this for both datasets everytime we want to re-run this notebook or the analyses in the other notebook, I’ve set this up to save the MAF objects as RDS files.

First let’s establish the file paths.

# File paths for the needed files for this analysis
metadata_dir <- file.path(scratch_dir, "metadata_filtered_maf_samples.tsv")
strelka2_dir <- file.path(scratch_dir, "strelka2.RDS")
mutect2_dir <- file.path(scratch_dir, "mutect2.RDS")

Read in the Strelka2 and Mutect2 data

We will read in the data as maftools objects from an RDS file, unless maftools has not been run on them yet. We will use metadata as the clinicalData for the maftools object.

Note: If you trying to run the initial set up step in a Docker container, it will likely be out of memory killed, unless you have ~50GB you can allocate to Docker.

# Get a vector of whether these exist
files_needed <- file.exists(metadata_dir, strelka2_dir, mutect2_dir)

if (all(files_needed)) {
  # Read the ready-to-go files if these files exist
  metadata <- metadata <- readr::read_tsv(metadata_dir)
  strelka2 <- readRDS(strelka2_dir)
  mutect2 <- readRDS(mutect2_dir)
} else { # If any of the needed files don't exist, rerun this process:
  # Only import the sample names
  strelka2_samples <- data.table::fread(file.path(
    data_dir,
    "pbta-snv-strelka2.vep.maf.gz"
  ),
  select = "Tumor_Sample_Barcode",
  skip = 1,
  data.table = FALSE
  ) %>%
    dplyr::pull("Tumor_Sample_Barcode")

  mutect2_samples <- data.table::fread(file.path(
    data_dir,
    "pbta-snv-mutect2.vep.maf.gz"
  ),
  select = "Tumor_Sample_Barcode",
  skip = 1,
  data.table = FALSE
  ) %>%
    dplyr::pull("Tumor_Sample_Barcode")

  # Isolate metadata to only the samples that are in the datasets
  metadata <- readr::read_tsv(file.path(data_dir, "pbta-histologies.tsv")) %>%
    dplyr::filter(Kids_First_Biospecimen_ID %in% c(strelka2_samples, mutect2_samples)) %>%
    dplyr::distinct(Kids_First_Biospecimen_ID, .keep_all = TRUE) %>%
    dplyr::arrange() %>%
    dplyr::rename(Tumor_Sample_Barcode = Kids_First_Biospecimen_ID) %>%
    readr::write_tsv(file.path(scratch_dir, "metadata_filtered_maf_samples.tsv"))

  # Read in original strelka file with maftools
  strelka2 <- maftools::read.maf(file.path(data_dir, "pbta-snv-strelka2.vep.maf.gz"),
    clinicalData = metadata
  )

  # Save to RDS so we don't have to run this again
  saveRDS(strelka2, strelka2_dir)

  # Same for MuTect2
  mutect2 <- maftools::read.maf(file.path(data_dir, "pbta-snv-mutect2.vep.maf.gz"),
    clinicalData = metadata
  )
  saveRDS(mutect2, mutect2_dir)
}
Parsed with column specification:
cols(
  .default = col_character(),
  age_at_diagnosis = col_double(),
  molecular_subtype = col_logical()
)
See spec(...) for full column specifications.

Compare number of mutations per gene

gene_cors <- correlate_maf_summaries(maf1 = strelka2, 
                                     maf2 = mutect2, 
                                     maf1_name = "strelka2", 
                                     maf2_name = "mutect2",
                                     summarize_by = "gene")
Cannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with ties
# Print out this data.frame
gene_cors %>%
  ggplot2::ggplot(ggplot2::aes(x = variable, y = pearson.cor)) +
  ggplot2::geom_bar(stat = "identity") +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 90, hjust = 1))  

Compare number of mutations per sample

sample_cors <- correlate_maf_summaries(maf1 = strelka2, 
                                       maf2 = mutect2, 
                                       maf1_name = "strelka2", 
                                       maf2_name = "mutect2",
                                       summarize_by = "sample")
Cannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with tiesCannot compute exact p-value with ties
# Print out this data.frame
sample_cors %>%
  ggplot2::ggplot(ggplot2::aes(x = variable, y = pearson.cor)) +
  ggplot2::geom_bar(stat = "identity") +
  ggplot2::theme_classic() +
  ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 90, hjust = 1))  

Plot Transition/Transversions

These built in maftools functions plot the overall presence of transitions and transversions and is recommended by the maftools vignette as a method of obtaining an overall base change summary.

Strelka2 transition/transversion plot.

maftools::plotTiTv(maftools::titv(strelka2))

MuTect2 transition/transversion plot.

maftools::plotTiTv(maftools::titv(mutect2))

Set up new variables

Let’s set up Strelka2’s variables first.

# Use the premade function to create our new variables
strelka2_vaf <- set_up_variables(strelka2)
NAs introduced by coercion
# Take a look at this df
strelka2_vaf

Now we will do the same for MuTect2.

# Use the premade function to create our new variables
mutect2_vaf <- set_up_variables(mutect2)

# Take a look at this df
mutect2_vaf

Combine MuTect2 and Strelka2 data.frames into one data.frame

Join these datasets based on the mutation_id created by the set_up_variables function. Save to a TSV file to be used in the subsequent notebook.

# Merge these data.frames together
vaf_df <- strelka2_vaf %>%
  dplyr::full_join(mutect2_vaf,
    by = "mutation_id",
    suffix = c(".strelka2", ".mutect2")
  ) %>%
  # Make a variable that denotes which dataset it is in.
  dplyr::mutate(dataset = dplyr::case_when(
    is.na(Allele.mutect2) ~ "strelka2_only",
    is.na(Allele.strelka2) ~ "mutect2_only",
    TRUE ~ "both"
  )) %>%
  readr::write_tsv(file.path("results", "combined_results.tsv"))

Make a zipped up version that can be stored on GitHub.

zip(
  file.path("results", "combined_results.tsv.zip"),
  file.path("results", "combined_results.tsv")
)
updating: results/combined_results.tsv (deflated 85%)

Session Info:

sessionInfo()
R version 3.6.1 (2019-07-05)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS Mojave 10.14.5

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] parallel  stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] Biobase_2.44.0      BiocGenerics_0.30.0

loaded via a namespace (and not attached):
 [1] pkgload_1.0.2      tidyr_0.8.3        jsonlite_1.6       splines_3.6.1      foreach_1.4.7      assertthat_0.2.1   BiocManager_1.30.4
 [8] yaml_2.2.0         remotes_2.1.0      sessioninfo_1.1.1  pillar_1.4.2       backports_1.1.4    lattice_0.20-38    glue_1.3.1        
[15] digest_0.6.20      RColorBrewer_1.1-2 colorspace_1.4-1   htmltools_0.3.6    Matrix_1.2-17      plyr_1.8.4         pkgconfig_2.0.2   
[22] devtools_2.1.0     bibtex_0.4.2       purrr_0.3.2        xtable_1.8-4       scales_1.0.0       processx_3.4.1     tibble_2.1.3      
[29] pkgmaker_0.27      ggplot2_3.2.1      usethis_1.5.1      withr_2.1.2        hexbin_1.27.3      lazyeval_0.2.2     cli_1.1.0         
[36] survival_2.44-1.1  magrittr_1.5       crayon_1.3.4       memoise_1.1.0      evaluate_0.14      ps_1.3.0           fs_1.3.1          
[43] doParallel_1.0.15  NMF_0.21.0         pkgbuild_1.0.4     tools_3.6.1        registry_0.5-1     data.table_1.12.2  prettyunits_1.0.2 
[50] hms_0.5.0          gridBase_0.4-7     stringr_1.4.0      munsell_0.5.0      cluster_2.1.0      rngtools_1.4       maftools_2.0.15   
[57] callr_3.3.1        compiler_3.6.1     rlang_0.4.0        grid_3.6.1         iterators_1.0.12   rstudioapi_0.10    base64enc_0.1-3   
[64] labeling_0.3       rmarkdown_1.14     testthat_2.2.1     gtable_0.3.0       codetools_0.2-16   reshape2_1.4.3     R6_2.4.0          
[71] knitr_1.24         dplyr_0.8.3        zeallot_0.1.0      rprojroot_1.3-2    readr_1.3.1        desc_1.2.0         stringi_1.4.3     
[78] Rcpp_1.0.2         vctrs_0.2.0        wordcloud_2.6      tidyselect_0.2.5   xfun_0.8          
LS0tCnRpdGxlOiAiU2V0IHVwIGNvbWJpbmVkIGRhdGEgb2YgTXV0ZWN0MiBhbmQgU3RyZWxrYTIiCm91dHB1dDogICAKICBodG1sX25vdGVib29rOiAKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKQ2FuZGFjZSBTYXZvbmVuIC0gQ0NETCBmb3IgQUxTRgoKVGhpcyBub3RlYm9vayBzZXRzIHVwIHRoZSBbTUFGIGRhdGEgZmlsZXMgYXMgYSBjb21iaW5lZCBmaWxlIGZvciByZWFkeSBjb21wYXJpc29uIGluIDAyLWFuYWx5emUtY29uY29yZGFuY2UuUm1kXShodHRwczovL2dpdGh1Yi5jb20vQWxleHNMZW1vbmFkZS9PcGVuUEJUQS1hbmFseXNpcy90cmVlL21hc3Rlci9hbmFseXNlcy9tdXRlY3QyLXZzLXN0cmVsa2EyLzAyLWFuYWx5emUtY29uY29yZGFuY2UuUm1kKQphbmQgYWxzbyBkb2VzIHNvbWUgZmlyc3QgbGluZSBhbmFseXNlcyBmcm9tIHJlYWR5LW1hZGUgYG1hZnRvb2xzYCBmdW5jdGlvbnMuCgpJdCBpcyB0aGUgZmlyc3Qgbm90ZWJvb2sgaW4gdGhpcyBzZXJpZXMgd2hpY2ggYWRkcmVzc2VzIFtpc3N1ZSBcIyAzMCBpbiBPcGVuUEJUQV0oaHR0cHM6Ly9naXRodWIuY29tL0FsZXhzTGVtb25hZGUvT3BlblBCVEEtYW5hbHlzaXMvaXNzdWVzLzMwKS4KCiMjIyBTdW1tYXJ5IG9mIHRoZSBTZXQgVXA6ICAKCkZvciBib3RoIHRoZSBTdHJlbGthMiBhbmQgTXVUZWN0MiBkYXRhc2V0cywgdGhlIGRhdGEgaXMgaW1wb3J0ZWQgYnkgCmBtYWZ0b29sczo6cmVhZC5tYWZgIGFuZCB0aGUgY29ycmVzcG9uZGluZyBjbGluaWNhbCBkYXRhIGZyb20gCmBwYnRhLWhpc3RvbG9naWVzLnRzdmAgaXMgYWRkZWQgdG8gdGhlIG9iamVjdC4gClRoaXMgaXMgb25seSBkb25lIG9uY2UgYXMgd3JpdHRlbiAoYXMgdGhlIGByZWFkLm1hZmAgaXMgdmVyeSBtZW1vcnkgaW50ZW5zaXZlKQphbmQgZWFjaCBNdVRlY3QyIGFuZCBTdHJlbGthMiBhcmUgc2F2ZWQgdG8gYW4gUkRTIGZpbGUgZm9yIGZhc3RlciBhbmQgcmVhZHkgCnJlbG9hZGluZy4KCkZvciBib3RoIFN0cmVsa2EyIGFuZCBNdVRlY3QyIGRhdGEsIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIGFyZSBjYWxjdWxhdGVkCmFuZCBhZGRlZCBpbnRvIHRoZSBjb21iaW5lZCBkYXRhc2V0IGZvciBhbmFseXNpcyBpbiBbMDItYW5hbHl6ZS1jb25jb3JkYW5jZS5SbWRdKGh0dHBzOi8vZ2l0aHViLmNvbS9BbGV4c0xlbW9uYWRlL09wZW5QQlRBLWFuYWx5c2lzL3RyZWUvbWFzdGVyL2FuYWx5c2VzL211dGVjdDItdnMtc3RyZWxrYTIvMDItYW5hbHl6ZS1jb25jb3JkYW5jZS5SbWQpLgoKIyMjIyBWYXJpYWJsZXMgY3JlYXRlZDogIAoKLSBgdmFmYCA6IFZhcmlhbnQgQWxsZWxlIEZyZXF1ZW5jeSA9IGAodF9hbHRfY291bnQpIC8gKHRfcmVmX2NvdW50ICsgdF9hbHRfY291bnQpYC4gIAotIGBtdXRhdGlvbl9pZGA6IFVzZWQgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdHdvIHZhcmlhbnRzIGNhbGxzIGFyZSBpZGVudGljYWwgCmluIGJvdGggZGF0YXNldHMuIApJdCBpcyBhIGNvbmNhdGVuYXRpb24gb2Y6IGBIdWdvX1N5bWJvbGAsIGBjaGFuZ2VgLCBgU3RhcnRfUG9zaXRpb25gLCBhbmQgCmBUdW1vcl9TYW1wbGVfQmFyY29kZWAgKHRoZSBzYW1wbGUgSUQpLiAgIAotIGBiYXNlX2NoYW5nZWA6IHZhcmlhYmxlIHRoYXQgaW5kaWNhdGVzIHRoZSBleGFjdCBjaGFuZ2UgaW4gYmFzZXMgKGUuZy4gJ1Q+QycpLiAgCi0gYGNoYW5nZWA6IGluZGljYXRlcyB0aGUgYGJhc2VfY2hhbmdlYCBpbmZvcm1hdGlvbiBidXQgZ3JvdXBzIHRvZ2V0aGVyIApkZWxldGlvbnMsIGluc2VydGlvbnMsIGFuZCBsb25nIChtb3JlIHRoYW4gYSBTTlYpIGFzIHRoZWlyIG93biBncm91cHMuICAKLSBgY29kaW5nYDogU3VtbWFyaXplIHRoZSBgQklPVFlQRWAgdmFyaWFibGUgZm9yIHdoZXRoZXIgb3Igbm90IGl0IGlzIGEgY29kaW5nIGdlbmUuICAKCiMjIyMgVGhlIG91dHB1dCBmaWxlcyBmcm9tIHRoaXMgbm90ZWJvb2s6CgotIGBzY3JhdGNoL3N0cmVsa2EyLlJEU2AgIAotIGBzY3JhdGNoL211dGVjdDIuUkRTYCAgCi0gYHNjcmF0Y2gvbWV0YWRhdGFfZmlsdGVyZWRfbWFmX3NhbXBsZXMudHN2YCAgCi0gYGFuYWx5c2VzL211dGVjdDItdnMtc3RyZWxrYTIvcGxvdHMvc2FtcGxlX2Nvcl9tdXRlY3QyX3ZzX3N0cmVsa2EyLnBkZmAKLSBgYW5hbHlzZXMvbXV0ZWN0Mi12cy1zdHJlbGthMi9wbG90cy9zYW1wbGVfY29yX211dGVjdDJfdnNfc3RyZWxrYTIucGRmYAotIGBhbmFseXNlcy9tdXRlY3QyLXZzLXN0cmVsa2EyL3Jlc3VsdHMvY29tYmluZWRfcmVzdWx0cy50c3ZgICAKCiMjIFVzYWdlCgpUbyBydW4gdGhpcyBmcm9tIHRoZSBjb21tYW5kIGxpbmUsIHVzZToKYGBgClJzY3JpcHQgLWUgInJtYXJrZG93bjo6cmVuZGVyKCdhbmFseXNlcy9tdXRlY3QyLXZzLXN0cmVsa2EyLzAxLXNldC11cC5SbWQnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xlYW4gPSBUUlVFKSIKYGBgCiBfVGhpcyBhc3N1bWVzIHlvdSBhcmUgaW4gdGhlIHRvcCBkaXJlY3Rvcnkgb2YgdGhlIHJlcG9zaXRvcnkuXwoKIyMgU2V0IFVwCgpgYGB7cn0KIyBXZSBuZWVkIG1hZnRvb2xzIC0gdGhpcyB3aWxsIGJlIGFkZGVkIHRvIHRoZSBydW5uaW5nIERvY2tlciBpc3N1ZSB3aGVuZXZlciBpdCBpcyB1cAppZiAoISgibWFmdG9vbHMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSB7CiAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogIH0KICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgibWFmdG9vbHMiKQp9CgojIFdpbGwgbmVlZCBoZXhiaW4gZm9yIHRoZSBoZXggcGxvdAppZiAoISgiaGV4YmluIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSkgewogIGluc3RhbGwucGFja2FnZXMoImhleGJpbiIpCn0KYGBgCgpHZXQgYG1hZ3JpdHRyYCBwaXBlCgpgYGB7cn0KYCU+JWAgPC0gZHBseXI6OmAlPiVgCmBgYAoKIyMjIERpcmVjdG9yaWVzIGFuZCBmaWxlcwoKUGF0aCB0byB0aGUgc3ltbGlua2VkIGRhdGEgb2J0YWluZWQgdmlhIGBiYXNoIGRvd25sb2FkLWRhdGEuc2hgLgoKYGBge3J9CmRhdGFfZGlyIDwtIGZpbGUucGF0aCgiLi4iLCAiLi4iLCAiZGF0YSIpCnNjcmF0Y2hfZGlyIDwtIGZpbGUucGF0aCgiLi4iLCAiLi4iLCAic2NyYXRjaCIpCmBgYAoKQ3JlYXRlIG91dHB1dCBkaXJlY3RvcmllcyBpbiB0aGlzIGFuYWx5c2lzIGZvbGRlci4KCmBgYHtyfQppZiAoIWRpci5leGlzdHMoInJlc3VsdHMiKSkgewogIGRpci5jcmVhdGUoInJlc3VsdHMiKQp9CmlmICghZGlyLmV4aXN0cygicGxvdHMiKSkgewogIGRpci5jcmVhdGUoInBsb3RzIikKfQpgYGAKCiMjIyBGdW5jdGlvbnMKClNldCB1cCB0aGUgZnVuY3Rpb24gdGhhdCB3aWxsIGNyZWF0ZSB0aGUgbmV3IHZhcmlhYmxlcyBmcm9tIHRoZSBtYWYgb2JqZWN0cy4gCgojIyMjIERlc2NyaXB0aW9uIG9mIHZhcmlhYmxlcyAKVGhlIGZ1bmN0aW9uLCBgc2V0X3VwX3ZhcmlhYmxlc2AsIHdpbGwgY2FsY3VsYXRlIHRoZSBmb2xsb3dpbmcgdmFyaWFibGVzIGZyb20gYSBtYWYgb2JqZWN0OiAgCi0gQ2FsY3VsYXRlIFZBRiBmb3IgZWFjaC4gClRoZSBWQUZzIGZvciBlYWNoIHZhcmlhdGlvbiBpbiBlYWNoIGRhdGFzZXQgYXJlIGNhbGN1bGF0ZWQgYnkgCmB0X2FsdF9jb3VudCkgLyAodF9yZWZfY291bnQgKyB0X2FsdF9jb3VudCkgYCwgYXMgZm9sbG93aW5nIHRoZSBbY29kZSB1c2VkIGluIApgbWFmdG9vbHNgXShodHRwczovL2dpdGh1Yi5jb20vUG9pc29uQWxpZW4vbWFmdG9vbHMvYmxvYi8xZDAyNzBlMzVjMmUwZjQ5MzA5ZWJhMDhiNjIzNDNhYzBkYjEwNTYwL1IvcGxvdF92YWYuUiNMMzkpICAKLSBDcmVhdGUgYSBgYmFzZV9jaGFuZ2VgIHZhcmlhYmxlIHRoYXQgaW5kaWNhdGVzIHRoZSBleGFjdCBjaGFuZ2UgaW4gYmFzZXMuICAKLSBDcmVhdGUgYSBgY2hhbmdlYCB2YXJpYWJsZSBmb3IgZWFjaCB3aGljaCBpbmRpY2F0ZXMgdGhlIGBiYXNlX2NoYW5nZWAKaW5mb3JtYXRpb24gYnV0IGdyb3VwcyB0b2dldGhlciBkZWxldGlvbnMsIGluc2VydGlvbnMsIGFuZCBsb25nIChtb3JlIHRoYW4gYSAKU05WKSBhcyB0aGVpciBvd24gZ3JvdXBzLiAgCi0gTWFrZSBhIG11dGF0aW9uIGlkIGJ5IGNvbmNhdGVuYXRpbmcgYEh1Z29fU3ltYm9sYCwgYGNoYW5nZWAsIGBTdGFydF9Qb3NpdGlvbmAsCmFuZCBgVHVtb3JfU2FtcGxlX0JhcmNvZGVgICh0aGUgc2FtcGxlIElEKS4gICAKV2Ugd2lsbCB1c2UgdGhpcyB2YXJpYWJsZSB0byBkZXRlcm1pbmUgd2hldGhlciB0d28gdmFyaWFudHMgY2FsbHMgYXJlIGlkZW50aWNhbCAKaW4gYm90aCBkYXRhc2V0cy4gCi0gU3VtbWFyaXplIHRoZSBgQklPVFlQRWAgdmFyaWFibGUgZm9yIHdoZXRoZXIgb3Igbm90IGl0IGlzIGEgY29kaW5nIGdlbmUuICAgCgpgYGB7cn0Kc2V0X3VwX3ZhcmlhYmxlcyA8LSBmdW5jdGlvbihtYWYgPSBOVUxMKSB7CiAgIyBDcmVhdGVzIHRoZXNlIG5ldyB2YXJpYWJsZXMgZnJvbSBhIG1hZiBvYmplY3QgcHJvdmlkZWQ6IFZBRiwgbXV0YXRpb25faWQsIAogICMgYmFzZV9jaGFuZ2UsIGNoYW5nZSwgY29kaW5nLgogICMKICAjIEFyZ3M6CiAgIyAgIG1hZjogYSBtYWYgb2JqZWN0IHRvIGNyZWF0ZSBuZXcgdmFyaWFibGVzIGZyb20KICAjCiAgIyBSZXR1cm5zOgogICMgICBhIGRhdGEuZnJhbWUgd2l0aCBhbGwgdGhlIG9yaWdpbmFsIGluZm9ybWF0aW9uIGluIHRoZSBgQGRhdGFgIHBhcnQgb2YgdGhlIAogICMgICBtYWYgb2JqZWN0IGJ1dCB3aXRoIHRoZXNlIG5ldyB2YXJpYWJsZXM6IFZBRiwgbXV0YXRpb25faWQsIGJhc2VfY2hhbmdlLCAKICAjICAgY2hhbmdlLCBjb2RpbmcuCgogICMgRXh0cmFjdCB0aGUgZGF0YSBwYXJ0IG9mIHRoZSBtYWYgb2JqZWN0LCBwdXQgaXQgdGhyb3VnaCBhIGRwbHlyIHBpcGUuIAogIGRmIDwtIG1hZkBkYXRhCiAgZGYgJT4lCiAgICBkcGx5cjo6bXV0YXRlKAogICAgICAjIENhbGN1bGF0ZSB0aGUgdmFyaWFudCBhbGxlbGUgZnJlcXVlbmN5CiAgICAgIHZhZiA9IGFzLm51bWVyaWModF9hbHRfY291bnQpIC8gKGFzLm51bWVyaWModF9yZWZfY291bnQpICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5udW1lcmljKHRfYWx0X2NvdW50KSksCiAgICAgICMgQ3JlYXRlIGEgYmFzZV9jaGFuZ2UgdmFyaWFibGUKICAgICAgYmFzZV9jaGFuZ2UgPSBwYXN0ZTAoUmVmZXJlbmNlX0FsbGVsZSwgIj4iLCBBbGxlbGUpLAogICAgICAKICAgICAgIyBDcmVhdGUgYSB2YXJpYWJsZSB0aGF0IG5vdGVzIHdoZXRoZXIgdGhlIHZhcmlhbnQgaXMgaW4gYSAKICAgICAgIyBjb2Rpbmcvbm9uLWNvZGluZyByZWdpb24KICAgICAgY29kaW5nID0gZHBseXI6OmNhc2Vfd2hlbigKICAgICAgICBCSU9UWVBFICE9ICJwcm90ZWluX2NvZGluZyIgfiAibm9uLWNvZGluZyIsCiAgICAgICAgVFJVRSB+ICJwcm90ZWluX2NvZGluZyIKICAgICAgKQogICAgKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoCiAgICAgICMgRnJvbSB0aGUgYmFzZV9jaGFuZ2UgdmFyaWFibGUsIHN1bW1hcml6ZSBpbnNlcnRpb25zLCBkZWxldGlvbnMsIGFuZCAKICAgICAgIyBjaGFuZ2VzIHRoYXQgYXJlIG1vcmUgdGhhbiBvbmUgYmFzZSBpbnRvIHRoZWlyIG93biBncm91cHMuCiAgICAgIGNoYW5nZSA9IGRwbHlyOjpjYXNlX3doZW4oCiAgICAgIGdyZXBsKCJeLSIsIGJhc2VfY2hhbmdlKSB+ICJpbnNlcnRpb24iLAogICAgICBncmVwbCgiLSQiLCBiYXNlX2NoYW5nZSkgfiAiZGVsZXRpb24iLAogICAgICBuY2hhcihiYXNlX2NoYW5nZSkgPiAzIH4gImxvbmdfY2hhbmdlIiwKICAgICAgVFJVRSB+IGJhc2VfY2hhbmdlCiAgICApKSAlPiUKICAgIGRwbHlyOjptdXRhdGUoCiAgICAgICMgQ3JlYXRlIHRoZSBtdXRhdGlvbiBpZCBiYXNlZCBvbiB0aGUgY2hhbmdlIHZhcmlhYmxlIGFzIHdlbGwgYXMgdGhlIAogICAgICAjIGdlbmUgc3ltYm9sLCBzdGFydCBwb3NpdGlvbiwgYW5kIHNhbXBsZSBJRC4gCiAgICAgIG11dGF0aW9uX2lkID0gcGFzdGUwKAogICAgICAgIEh1Z29fU3ltYm9sLCAiXyIsCiAgICAgICAgY2hhbmdlLCAiXyIsCiAgICAgICAgU3RhcnRfUG9zaXRpb24sICJfIiwKICAgICAgICBUdW1vcl9TYW1wbGVfQmFyY29kZQogICAgICApCiAgICApICU+JQogICAgIyBHZXQgcmlkIG9mIGFueSB2YXJpYWJsZXMgdGhhdCBoYXZlIGNvbXBsZXRlbHkgTkFzLgogICAgZHBseXI6OnNlbGVjdCgtd2hpY2goYXBwbHkoaXMubmEoLiksIDIsIGFsbCkpKQp9CmBgYAoKIyMjIyBTdW1tYXJpemUgYW5kIGNvbXBhcmUgYG1hZmAgb2JqZWN0cyBmdW5jdGlvbiAKClRoaXMgZnVuY3Rpb24gdXNlcyB0aGUgbWFmdG9vbHMgc3VtbWFyeSBmdW5jdGlvbnMsIGBtYWZ0b29sczo6Z2V0R2VuZVN1bW1hcnlgIG9yCmBtYWZ0b29sczo6Z2V0U2FtcGxlU3VtbWFyeWAsIHRvIHN1bW1hcml6ZSBhbmQgY29tcGFyZSB0d28gbWFmIG9iamVjdHMuClRoZXNlIGBtYWZ0b29sc2Agc3VtbWFyeSBmdW5jdGlvbnMgb2J0YWluIHRoZSBudW1iZXIgb2YgdmFyaWFudHMgb2YgZWFjaCAKY2xhc3NpZmljYXRpb24gdHlwZSBvbiBlaXRoZXIgYSBnZW5lIG9yIHNhbXBsZSBsZXZlbCAodGhpcyBpcyB0YWtlZCBmcm9tIHRoZSAKaW5mb3JtYXRpb24gaW4gdGhlIFtgVmFyaWFudF9DbGFzc2lmaWNhdGlvbmAgZmllbGQgaW4gdGhlIG9yaWdpbmFsIG1hZiBmaWxlXShodHRwczovL2RvY3MuZ2RjLmNhbmNlci5nb3YvRGF0YS9GaWxlX0Zvcm1hdHMvTUFGX0Zvcm1hdC8pLikKVGhpcyBmaWVsZCBoYXMgdGhlIHRyYW5zbGF0aW9uIGVmZmVjdCBvZiB0aGUgdmFyaWFudCwgZS5nLiAiRnJhbWVfU2hpZnRfRGVsIi4KCmBgYHtyfQpjb3JyZWxhdGVfbWFmX3N1bW1hcmllcyA8LSBmdW5jdGlvbihtYWYxID0gTlVMTCwgbWFmMiA9IE5VTEwsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWYxX25hbWUgPSAibWFmMSIsIG1hZjJfbmFtZSA9ICJtYWYyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcml6ZV9ieSA9ICJnZW5lIikgewogICMgVGFrZXMgdHdvIG1hZiBmaWxlcywgc3VtbWFyaXplcyB0aGVtIGJ5IGdlbmUgb3Igc2FtcGxlLCBjb21iaW5lcyB0aGVtIGJ5IAogICMgYSBmdWxsIGpvaW4sIGFuZCBjb3JyZWxhdGVzIHRoZSBzdW1tYXJpZXMgdXNpbmcgUGVhcnNvbidzIGFuZCBTcGVhcm1hbidzCiAgIyBjb3JyZWxhdGlvbnMuIAogICMKICAjIEFyZ3M6CiAgIyAgIG1hZjE6IGEgZmlyc3QgbWFmIG9iamVjdCB0byBzdW1tYXJpemUgYW5kIGNvbXBhcmUgdG8gbWFmMgogICMgICBtYWYyOiBhIHNlY29uZCBtYWYgb2JqZWN0IHRvIHN1bW1hcml6ZSBhbmQgY29tcGFyZSB0byBtYWYxCiAgIyAgIG1hZjFfbmFtZTogYSBjaGFyYWN0ZXIgc3RyaW5nIHRoYXQgaW5kaWNhdGVzIHdoYXQgbGFiZWwgeW91IHdvdWxkIGxpa2UgdG8gCiAgIyAgIHVzZSBmb3IgbWFmMSdzIGRhdGEuIAogICMgICBtYWYyX25hbWU6IGEgY2hhcmFjdGVyIHN0cmluZyB0aGF0IGluZGljYXRlcyB3aGF0IGxhYmVsIHlvdSB3b3VsZCBsaWtlIHRvIAogICMgICB1c2UgZm9yIG1hZjIncyBkYXRhLiAKICAjICAgc3VtbWFyaXplX2J5OiBhIHNpbmdsZSBjaGFyYWN0ZXIgc3RyaW5nIHNpZ25pZnlpbmcgZWl0aGVyICJnZW5lIiBvciAic2FtcGxlIgogICMgICAgICAgVGhpcyB3aWxsIGluZGljYXRlIHdoZXRoZXIgdG8gc3VtbWFyaXplIGNvbXBhcmUgYnkgZ2VuZSBvciBzYW1wbGUuIAogICMgICAgICAgImdlbmUiIHdpbGwgc3VtbWFyaXplIGJvdGggbWFmMSBhbmQgbWFmMiB1c2luZyBtYWZ0b29sczo6Z2V0R2VuZVN1bW1hcnkKICAjICAgICAgICJzYW1wbGUiIHdpbGwgc3VtbWFyaXplIGJvdGggbWFmMSBhbmQgbWFmMiB1c2luZyBtYWZ0b29sczo6Z2V0U2FtcGxlU3VtbWFyeQogICMgUmV0dXJuczoKICAjICAgYSBkYXRhLmZyYW1lIHRoYXQgY29udGFpbnMgdGhlIGNvcnJlbGF0aW9uIHIgYW5kIHAgdmFsdWVzIGZvciBib3RoIAogICMgICBtYWYxIGFuZCBtYWYyIGJhc2VkIG9uIHRoZSBudW1iZXJzIG9mIHZhcmlhbnRzIG9mIGVhY2ggY2xhc3MuCiAgCiAgIyBTdW1tYXJpemUgdGhlIG1hZiBvYmplY3RzIGJ5IHRoZSBzcGVjaWZpZWQgYXJndW1lbnQKICBpZiAoc3VtbWFyaXplX2J5ID09ICJnZW5lIikgewogICAgbWFmMV9zdW0gPC0gbWFmdG9vbHM6OmdldEdlbmVTdW1tYXJ5KG1hZjEpCiAgICBtYWYyX3N1bSA8LSBtYWZ0b29sczo6Z2V0R2VuZVN1bW1hcnkobWFmMikKICAgIGtleSA8LSAiSHVnb19TeW1ib2wiCiAgfQogIGlmIChzdW1tYXJpemVfYnkgPT0gInNhbXBsZSIpIHsKICAgIG1hZjFfc3VtIDwtIG1hZnRvb2xzOjpnZXRTYW1wbGVTdW1tYXJ5KG1hZjEpCiAgICBtYWYyX3N1bSA8LSBtYWZ0b29sczo6Z2V0U2FtcGxlU3VtbWFyeShtYWYyKQogICAga2V5IDwtICJUdW1vcl9TYW1wbGVfQmFyY29kZSIKICB9CiAgCiAgIyBEbyBhIGZ1bGwgam9pbiBvZiBib3RoIHN1bW1hcmllcy4KICBtYWYxX3N1bSAlPiUKICAgIGRwbHlyOjpmdWxsX2pvaW4obWFmMl9zdW0sIGJ5ID0ga2V5KSAlPiUKICAgICMgTWVsdCB0aGlzIGRhdGEuZnJhbWUgc28gd2UgY2FuIG1ha2UgaXQgbG9uZyBmb3JtYXQKICAgIHJlc2hhcGUyOjptZWx0KGlkID0ga2V5KSAlPiUKICAgIGRwbHlyOjptdXRhdGUoZGF0YXNldCA9IGFzLmNoYXJhY3RlcihncmVwbCgiLngkIiwgdmFyaWFibGUpKSkgJT4lCiAgICAjIE1ha2UgYSBuZXcgY29sdW1uIHRoYXQgc3BlY2lmaWVzIHdoYXQgbWFmIG9iamVjdCB0aGUgZGF0YSBpcyBmcm9tCiAgICBkcGx5cjo6bXV0YXRlKGRhdGFzZXQgPSBkcGx5cjo6cmVjb2RlKGRhdGFzZXQsCiAgICAgIGBUUlVFYCA9IG1hZjFfbmFtZSwKICAgICAgYEZBTFNFYCA9IG1hZjJfbmFtZSwKICAgICkpICU+JQogICAgIyBHZXRzIHJpZCBvZiB0aGUgIi54IiBhbmQgIi55IiBzcGVjaWZpY2F0aW9ucyBvZiB0aGUgY29sdW1uIG5hbWVzCiAgICBkcGx5cjo6bXV0YXRlKHZhcmlhYmxlID0gZ3N1YigiLngkfC55JCIsICIiLCB2YXJpYWJsZSkpICU+JQogICAgIyBTcHJlYWRzIHRoZSBkYXRhIGJhc2VkIG9uIHRoZSBuZXcgZGF0YXNldCB2YXJpYWJsZQogICAgdGlkeXI6OnNwcmVhZCgiZGF0YXNldCIsICJ2YWx1ZSIpICU+JSAgCiAgICAKICAgICMgR2V0IGNvcnJlbGF0aW9ucyBieSBlYWNoIHR5cGUgb2YgdmFyaWFudCBjbGFzc2lmaWNhdGlvbgogICAgZHBseXI6Omdyb3VwX2J5KHZhcmlhYmxlKSAlPiUKICAgIGRwbHlyOjpzdW1tYXJpemUocGVhcnNvbi5jb3IgPSBjb3IudGVzdChzdHJlbGthMiwgbXV0ZWN0MiwgbWV0aG9kID0gInBlYXJzb24iKSRlc3RpbWF0ZSwKICAgICAgICAgICAgICAgICAgICAgcGVhcnNvbi5wdmFsID0gY29yLnRlc3Qoc3RyZWxrYTIsIG11dGVjdDIsIG1ldGhvZCA9ICJwZWFyc29uIikkcC52YWx1ZSwKICAgICAgICAgICAgICAgICAgICAgc3BlYXJtYW4uY29yID0gY29yLnRlc3Qoc3RyZWxrYTIsIG11dGVjdDIsIG1ldGhvZCA9ICJzcGVhcm1hbiIpJGVzdGltYXRlLAogICAgICAgICAgICAgICAgICAgICBzcGVhcm1hbi5wdmFsID0gY29yLnRlc3Qoc3RyZWxrYTIsIG11dGVjdDIsIG1ldGhvZCA9ICJwZWFyc29uIikkcC52YWx1ZSkgCn0KYGBgCgoKIyMgUmVhZCBpbiB0aGUgbWV0YWRhdGEgaW5mb3JtYXRpb24KClJ1bm5pbmcgYG1hZnRvb2xzOjpyZWFkLm1hZmAgdGFrZXMgYSBsb3Qgb2YgY29tcHV0aW5nIHBvd2VyIGFuZCB0aW1lLCBzbyB0byAKYXZvaWQgaGF2aW5nIHRvIHJ1biB0aGlzIGZvciBib3RoIGRhdGFzZXRzIGV2ZXJ5dGltZSB3ZSB3YW50IHRvIHJlLXJ1biB0aGlzIApub3RlYm9vayBvciB0aGUgYW5hbHlzZXMgaW4gdGhlIG90aGVyIG5vdGVib29rLCBJJ3ZlIHNldCB0aGlzIHVwIHRvIHNhdmUgdGhlIApgTUFGYCBvYmplY3RzIGFzIGBSRFNgIGZpbGVzLgoKRmlyc3QgbGV0J3MgZXN0YWJsaXNoIHRoZSBmaWxlIHBhdGhzLgoKYGBge3J9CiMgRmlsZSBwYXRocyBmb3IgdGhlIG5lZWRlZCBmaWxlcyBmb3IgdGhpcyBhbmFseXNpcwptZXRhZGF0YV9kaXIgPC0gZmlsZS5wYXRoKHNjcmF0Y2hfZGlyLCAibWV0YWRhdGFfZmlsdGVyZWRfbWFmX3NhbXBsZXMudHN2IikKc3RyZWxrYTJfZGlyIDwtIGZpbGUucGF0aChzY3JhdGNoX2RpciwgInN0cmVsa2EyLlJEUyIpCm11dGVjdDJfZGlyIDwtIGZpbGUucGF0aChzY3JhdGNoX2RpciwgIm11dGVjdDIuUkRTIikKYGBgCgojIyBSZWFkIGluIHRoZSBTdHJlbGthMiBhbmQgTXV0ZWN0MiBkYXRhCgpXZSB3aWxsIHJlYWQgaW4gdGhlIGRhdGEgYXMgYG1hZnRvb2xzYCBvYmplY3RzIGZyb20gYW4gUkRTIGZpbGUsIHVubGVzcyAKYG1hZnRvb2xzYCBoYXMgbm90IGJlZW4gcnVuIG9uIHRoZW0geWV0LgpXZSB3aWxsIHVzZSBgbWV0YWRhdGFgIGFzIHRoZSBgY2xpbmljYWxEYXRhYCBmb3IgdGhlIGBtYWZ0b29sc2Agb2JqZWN0LiAKIApOb3RlOiBJZiB5b3UgdHJ5aW5nIHRvIHJ1biB0aGUgaW5pdGlhbCBzZXQgdXAgc3RlcCBpbiBhIERvY2tlciBjb250YWluZXIsIGl0IAp3aWxsIGxpa2VseSBiZSBvdXQgb2YgbWVtb3J5IGtpbGxlZCwgdW5sZXNzIHlvdSBoYXZlIH41MEdCIHlvdSBjYW4gYWxsb2NhdGUgdG8gCkRvY2tlci4gCgpgYGB7cn0KIyBHZXQgYSB2ZWN0b3Igb2Ygd2hldGhlciB0aGVzZSBleGlzdApmaWxlc19uZWVkZWQgPC0gZmlsZS5leGlzdHMobWV0YWRhdGFfZGlyLCBzdHJlbGthMl9kaXIsIG11dGVjdDJfZGlyKQoKaWYgKGFsbChmaWxlc19uZWVkZWQpKSB7CiAgIyBSZWFkIHRoZSByZWFkeS10by1nbyBmaWxlcyBpZiB0aGVzZSBmaWxlcyBleGlzdAogIG1ldGFkYXRhIDwtIG1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihtZXRhZGF0YV9kaXIpCiAgc3RyZWxrYTIgPC0gcmVhZFJEUyhzdHJlbGthMl9kaXIpCiAgbXV0ZWN0MiA8LSByZWFkUkRTKG11dGVjdDJfZGlyKQp9IGVsc2UgeyAjIElmIGFueSBvZiB0aGUgbmVlZGVkIGZpbGVzIGRvbid0IGV4aXN0LCByZXJ1biB0aGlzIHByb2Nlc3M6CiAgIyBPbmx5IGltcG9ydCB0aGUgc2FtcGxlIG5hbWVzCiAgc3RyZWxrYTJfc2FtcGxlcyA8LSBkYXRhLnRhYmxlOjpmcmVhZChmaWxlLnBhdGgoCiAgICBkYXRhX2RpciwKICAgICJwYnRhLXNudi1zdHJlbGthMi52ZXAubWFmLmd6IgogICksCiAgc2VsZWN0ID0gIlR1bW9yX1NhbXBsZV9CYXJjb2RlIiwKICBza2lwID0gMSwKICBkYXRhLnRhYmxlID0gRkFMU0UKICApICU+JQogICAgZHBseXI6OnB1bGwoIlR1bW9yX1NhbXBsZV9CYXJjb2RlIikKCiAgbXV0ZWN0Ml9zYW1wbGVzIDwtIGRhdGEudGFibGU6OmZyZWFkKGZpbGUucGF0aCgKICAgIGRhdGFfZGlyLAogICAgInBidGEtc252LW11dGVjdDIudmVwLm1hZi5neiIKICApLAogIHNlbGVjdCA9ICJUdW1vcl9TYW1wbGVfQmFyY29kZSIsCiAgc2tpcCA9IDEsCiAgZGF0YS50YWJsZSA9IEZBTFNFCiAgKSAlPiUKICAgIGRwbHlyOjpwdWxsKCJUdW1vcl9TYW1wbGVfQmFyY29kZSIpCgogICMgSXNvbGF0ZSBtZXRhZGF0YSB0byBvbmx5IHRoZSBzYW1wbGVzIHRoYXQgYXJlIGluIHRoZSBkYXRhc2V0cwogIG1ldGFkYXRhIDwtIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgoZGF0YV9kaXIsICJwYnRhLWhpc3RvbG9naWVzLnRzdiIpKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCAlaW4lIGMoc3RyZWxrYTJfc2FtcGxlcywgbXV0ZWN0Ml9zYW1wbGVzKSkgJT4lCiAgICBkcGx5cjo6ZGlzdGluY3QoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgICBkcGx5cjo6YXJyYW5nZSgpICU+JQogICAgZHBseXI6OnJlbmFtZShUdW1vcl9TYW1wbGVfQmFyY29kZSA9IEtpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQpICU+JQogICAgcmVhZHI6OndyaXRlX3RzdihmaWxlLnBhdGgoc2NyYXRjaF9kaXIsICJtZXRhZGF0YV9maWx0ZXJlZF9tYWZfc2FtcGxlcy50c3YiKSkKCiAgIyBSZWFkIGluIG9yaWdpbmFsIHN0cmVsa2EgZmlsZSB3aXRoIG1hZnRvb2xzCiAgc3RyZWxrYTIgPC0gbWFmdG9vbHM6OnJlYWQubWFmKGZpbGUucGF0aChkYXRhX2RpciwgInBidGEtc252LXN0cmVsa2EyLnZlcC5tYWYuZ3oiKSwKICAgIGNsaW5pY2FsRGF0YSA9IG1ldGFkYXRhCiAgKQoKICAjIFNhdmUgdG8gUkRTIHNvIHdlIGRvbid0IGhhdmUgdG8gcnVuIHRoaXMgYWdhaW4KICBzYXZlUkRTKHN0cmVsa2EyLCBzdHJlbGthMl9kaXIpCgogICMgU2FtZSBmb3IgTXVUZWN0MgogIG11dGVjdDIgPC0gbWFmdG9vbHM6OnJlYWQubWFmKGZpbGUucGF0aChkYXRhX2RpciwgInBidGEtc252LW11dGVjdDIudmVwLm1hZi5neiIpLAogICAgY2xpbmljYWxEYXRhID0gbWV0YWRhdGEKICApCiAgc2F2ZVJEUyhtdXRlY3QyLCBtdXRlY3QyX2RpcikKfQpgYGAKCiMjIENvbXBhcmUgbnVtYmVyIG9mIG11dGF0aW9ucyBwZXIgZ2VuZQoKYGBge3J9CmdlbmVfY29ycyA8LSBjb3JyZWxhdGVfbWFmX3N1bW1hcmllcyhtYWYxID0gc3RyZWxrYTIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFmMiA9IG11dGVjdDIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFmMV9uYW1lID0gInN0cmVsa2EyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWYyX25hbWUgPSAibXV0ZWN0MiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJpemVfYnkgPSAiZ2VuZSIpCgojIFByaW50IG91dCB0aGlzIGRhdGEuZnJhbWUKZ2VuZV9jb3JzICU+JQogIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IHZhcmlhYmxlLCB5ID0gcGVhcnNvbi5jb3IpKSArCiAgZ2dwbG90Mjo6Z2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC54ID0gZ2dwbG90Mjo6ZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICAKYGBgCgojIyBDb21wYXJlIG51bWJlciBvZiBtdXRhdGlvbnMgcGVyIHNhbXBsZQoKYGBge3J9CnNhbXBsZV9jb3JzIDwtIGNvcnJlbGF0ZV9tYWZfc3VtbWFyaWVzKG1hZjEgPSBzdHJlbGthMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hZjIgPSBtdXRlY3QyLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFmMV9uYW1lID0gInN0cmVsa2EyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hZjJfbmFtZSA9ICJtdXRlY3QyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXplX2J5ID0gInNhbXBsZSIpCgojIFByaW50IG91dCB0aGlzIGRhdGEuZnJhbWUKc2FtcGxlX2NvcnMgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KGdncGxvdDI6OmFlcyh4ID0gdmFyaWFibGUsIHkgPSBwZWFyc29uLmNvcikpICsKICBnZ3Bsb3QyOjpnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogIGdncGxvdDI6OnRoZW1lX2NsYXNzaWMoKSArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnggPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgIApgYGAKCiMjIFBsb3QgVHJhbnNpdGlvbi9UcmFuc3ZlcnNpb25zCgpUaGVzZSBidWlsdCBpbiBtYWZ0b29scyBmdW5jdGlvbnMgcGxvdCB0aGUgb3ZlcmFsbCBwcmVzZW5jZSBvZiB0cmFuc2l0aW9ucyBhbmQgCnRyYW5zdmVyc2lvbnMgYW5kIGlzIHJlY29tbWVuZGVkIGJ5IHRoZSBbbWFmdG9vbHMgdmlnbmV0dGVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9tYWZ0b29scy9pbnN0L2RvYy9tYWZ0b29scy5odG1sIzc0X3RyYW5zaXRpb25fYW5kX3RyYW5zdmVyc2lvbnMpIGFzIGEgbWV0aG9kIG9mIG9idGFpbmluZyBhbiBvdmVyYWxsIGJhc2UgY2hhbmdlCnN1bW1hcnkuIAoKIyMjIyBTdHJlbGthMiB0cmFuc2l0aW9uL3RyYW5zdmVyc2lvbiBwbG90LgoKYGBge3J9Cm1hZnRvb2xzOjpwbG90VGlUdihtYWZ0b29sczo6dGl0dihzdHJlbGthMikpCmBgYAoKIyMjIyBNdVRlY3QyIHRyYW5zaXRpb24vdHJhbnN2ZXJzaW9uIHBsb3QuCgpgYGB7cn0KbWFmdG9vbHM6OnBsb3RUaVR2KG1hZnRvb2xzOjp0aXR2KG11dGVjdDIpKQpgYGAKCiMjIFNldCB1cCBuZXcgdmFyaWFibGVzCgpMZXQncyBzZXQgdXAgU3RyZWxrYTIncyB2YXJpYWJsZXMgZmlyc3QuIAoKYGBge3J9CiMgVXNlIHRoZSBwcmVtYWRlIGZ1bmN0aW9uIHRvIGNyZWF0ZSBvdXIgbmV3IHZhcmlhYmxlcwpzdHJlbGthMl92YWYgPC0gc2V0X3VwX3ZhcmlhYmxlcyhzdHJlbGthMikKCiMgVGFrZSBhIGxvb2sgYXQgdGhpcyBkZgpzdHJlbGthMl92YWYKYGBgCgpOb3cgd2Ugd2lsbCBkbyB0aGUgc2FtZSBmb3IgTXVUZWN0Mi4KCmBgYHtyfQojIFVzZSB0aGUgcHJlbWFkZSBmdW5jdGlvbiB0byBjcmVhdGUgb3VyIG5ldyB2YXJpYWJsZXMKbXV0ZWN0Ml92YWYgPC0gc2V0X3VwX3ZhcmlhYmxlcyhtdXRlY3QyKQoKIyBUYWtlIGEgbG9vayBhdCB0aGlzIGRmCm11dGVjdDJfdmFmCmBgYAoKIyMgQ29tYmluZSBNdVRlY3QyIGFuZCBTdHJlbGthMiBkYXRhLmZyYW1lcyBpbnRvIG9uZSBkYXRhLmZyYW1lCgpKb2luIHRoZXNlIGRhdGFzZXRzIGJhc2VkIG9uIHRoZSBgbXV0YXRpb25faWRgIGNyZWF0ZWQgYnkgdGhlIGBzZXRfdXBfdmFyaWFibGVzYApmdW5jdGlvbi4gClNhdmUgdG8gYSBUU1YgZmlsZSB0byBiZSB1c2VkIGluIHRoZSBzdWJzZXF1ZW50IG5vdGVib29rLgoKYGBge3J9CiMgTWVyZ2UgdGhlc2UgZGF0YS5mcmFtZXMgdG9nZXRoZXIKdmFmX2RmIDwtIHN0cmVsa2EyX3ZhZiAlPiUKICBkcGx5cjo6ZnVsbF9qb2luKG11dGVjdDJfdmFmLAogICAgYnkgPSAibXV0YXRpb25faWQiLAogICAgc3VmZml4ID0gYygiLnN0cmVsa2EyIiwgIi5tdXRlY3QyIikKICApICU+JQogICMgTWFrZSBhIHZhcmlhYmxlIHRoYXQgZGVub3RlcyB3aGljaCBkYXRhc2V0IGl0IGlzIGluLgogIGRwbHlyOjptdXRhdGUoZGF0YXNldCA9IGRwbHlyOjpjYXNlX3doZW4oCiAgICBpcy5uYShBbGxlbGUubXV0ZWN0MikgfiAic3RyZWxrYTJfb25seSIsCiAgICBpcy5uYShBbGxlbGUuc3RyZWxrYTIpIH4gIm11dGVjdDJfb25seSIsCiAgICBUUlVFIH4gImJvdGgiCiAgKSkgJT4lCiAgcmVhZHI6OndyaXRlX3RzdihmaWxlLnBhdGgoInJlc3VsdHMiLCAiY29tYmluZWRfcmVzdWx0cy50c3YiKSkKYGBgCgpNYWtlIGEgemlwcGVkIHVwIHZlcnNpb24gdGhhdCBjYW4gYmUgc3RvcmVkIG9uIEdpdEh1Yi4KCmBgYHtyfQp6aXAoCiAgZmlsZS5wYXRoKCJyZXN1bHRzIiwgImNvbWJpbmVkX3Jlc3VsdHMudHN2LnppcCIpLAogIGZpbGUucGF0aCgicmVzdWx0cyIsICJjb21iaW5lZF9yZXN1bHRzLnRzdiIpCikKYGBgCgpTZXNzaW9uIEluZm86IAoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==